using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Db4objects.Db4o;
using Db4objects.Db4o.Constraints;
using Db4objects.Db4o.CS;
using Db4objects.Db4o.CS.Config;
using Db4objects.Db4o.IO;

namespace SharedObjects
{
	public class Db
	{
		private IObjectServer _server;
		private Stack<IObjectContainer> _clients;

		private const int NumInitialClients = 15;
#if USE_TEST_DB
			private const string databaseFile = "TheGameDatabaseTest.db4o";
#else
		private readonly string _databaseFile = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\TheGameDatabase.db4o";
#endif
		private IServerConfiguration _serverConfig;
		public Db()
		{
			InitializeServer();
		}

		private void InitializeServerConfig()
		{
			_serverConfig = Db4oClientServer.NewServerConfiguration();

			_serverConfig.File.Storage = new CachingStorage(new NonFlushingStorage(new FileStorage()), 1, 1024 * 1024 * 512); //cache with 500mb
			//serverConfig.File.Storage = new NonFlushingStorage(new FileStorage());//, 64, 1024*1024));
			_serverConfig.File.LockDatabaseFile = false;
			_serverConfig.Networking.SingleThreadedClient = true;

			_serverConfig.Common.ObjectClass(typeof(EntityBase)).ObjectField("<ID>k__BackingField").Indexed(true);
			_serverConfig.Common.ObjectClass(typeof(EntityBase)).CascadeOnUpdate(false);
			_serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(EntityBase), "<ID>k__BackingField"));
			var objects = from t in Assembly.GetExecutingAssembly().GetTypes() where t.IsClass && t.Namespace == "SharedObjects" && t.IsSubclassOf(typeof(EntityBase)) select t;
			foreach (var t in objects)
			{
				_serverConfig.Common.ObjectClass(t).ObjectField("<ID>k__BackingField").Indexed(true);
				_serverConfig.Common.ObjectClass(t).CascadeOnUpdate(false);
				_serverConfig.Common.Add(new UniqueFieldValueConstraint(t, "<ID>k__BackingField"));
			}
			#region old
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Account), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Agent), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Credential), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Bypasser), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ClientConfiguration), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ComputerSystem), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ComputerSystemAccount), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ComputerSystemFeature), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ComputerSystemLog), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Corporation), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(CPU), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Credential), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(CredentialFile), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(DnaCredential), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(FingerprintCredential), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(RetinalScanCredential), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(UsernamePasswordCredential), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ErrorLog), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Fragment), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Gateway), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(GatewayBlueprint), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(MemoryType), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(NetworkAddress), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(NetworkAdapter), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(Program), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ProgramCategory), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ProgramFile), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(SavedConnection), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(SecuritySystem), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(SecurityToken), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(SystemDirectory), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(SystemFile), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(UserProgram), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(UserProgramFragment), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(WorldData), "<ID>k__BackingField"));

			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(EmailAccount), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(EmailBody), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(EmailHeader), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(ShortEmailAccountInfo), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(TLD), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(BankAccount), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new UniqueFieldValueConstraint(typeof(BankTransaction), "<ID>k__BackingField"));
			//serverConfig.Common.Add(new TransparentActivationSupport());
			#endregion
		}

		private void InitializeServer()
		{
			InitializeServerConfig();

			_server = Db4oClientServer.OpenServer(_serverConfig, _databaseFile, 31336);
			_server.GrantAccess("admin", "admin");

			_clients = new Stack<IObjectContainer>();
			for (int i = 0; i < NumInitialClients; i++)
			{
				IObjectContainer client = _server.OpenClient();
				_clients.Push(client);
			}
		}

		public IObjectContainer ObtainClient()
		{
			IObjectContainer client = _clients.Pop();
			new Thread(
				new ThreadStart
					(
						delegate
						{
							lock (_clients)
							{
								IObjectContainer newClient = _server.OpenClient();// Db4oClientServer.OpenClient(clientConfig, "localhost", 31336, "admin", "admin");
								_clients.Push(newClient);
							}
						}
					)
				).Start();
			return client;
		}

		public void RecreateDatabase()
		{
			_server.Close();
			File.Delete(_databaseFile);
			InitializeServer();
		}

		public void Close()
		{
			foreach (IObjectContainer client in _clients)
				client.Close();
			_server.Close();
		}

		public void Flush()
		{
		}
	}
}